package com.hkyp.ss.course.provider.impl;

import com.hkyp.ss.course.apis.UserService;
import com.hkyp.ss.course.apis.cons.CommonConstants;
import com.hkyp.ss.course.apis.dto.request.*;
import com.hkyp.ss.course.apis.dto.response.UserAvatarRequestDTO;
import com.hkyp.ss.course.apis.dto.response.UserBaseInfoDTO;
import com.hkyp.ss.course.apis.dto.response.UserHomePageVO;
import com.hkyp.ss.course.apis.enumu.UserStatusEnum;
import com.hkyp.ss.course.apis.exception.BusinessException;
import com.hkyp.ss.course.apis.exception.DaoException;
import com.hkyp.ss.course.apis.exception.UnacceptableException;
import com.hkyp.ss.course.apis.utils.BeanMapper;
import com.hkyp.ss.course.apis.utils.JWTTokenUtil;
import com.hkyp.ss.course.apis.utils.third_party_api.EmailUtil;
import com.hkyp.ss.course.provider.model.User;
import com.hkyp.ss.course.provider.repository.UserRepository;
import com.hkyp.ss.course.provider.repository.mediate.UURelationRepository;
import com.hkyp.ss.course.provider.util.AESUtil;
import com.hkyp.ss.course.provider.util.Md5Util;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

//@Component
@Slf4j
@Service(version = "1.0")
@Transactional(rollbackFor = Exception.class)
public class UserServiceImpl implements UserService {

    @Autowired
    UserRepository userRepository;

    @Autowired
    UURelationRepository uuRelationRepository;

    @Override
    @Transactional(readOnly = true)
    public UserHomePageVO selectUserHomePage(Long userId) {

        User user= userRepository.findById(userId)
                         .orElse(null);
        if(user == null){
            return null;
        }

        UserHomePageVO userHomePageVO = BeanMapper.map(user, UserHomePageVO.class);

        Integer followingNum =  uuRelationRepository.countByFromId(userId);
        userHomePageVO.setFollowing(followingNum);

        return userHomePageVO;
    }

    @Override
    public void reSetPwdSendEmail(ResetPwd2RequestDTO RequestDTO) throws UnacceptableException {
        String newPassword = RequestDTO.getPassword();

        if(StringUtils.isEmpty(newPassword)){
            log.error("重置密码 密码入参为空");
            throw new UnacceptableException("失败");
        }
        if(!JWTTokenUtil.verifyWithNoId(RequestDTO.getToken())){
            log.error("重置密码token无效");
            throw new UnacceptableException("失败");
        }

        String innerToken = JWTTokenUtil.getInfo(RequestDTO.getToken(),"innerToken");
        if(StringUtils.isEmpty(innerToken)){
            log.error("重置密码innerToken为空");
            throw new UnacceptableException("失败");
        }
        String plain = "";
        //获取解密后的明文
        try {
            plain = AESUtil.RevertAESCode(innerToken);
        }catch (Exception e){
            log.error("重置密码 aes解密失败 ");
            throw new UnacceptableException("失败");
        }
        log.info("解密后的明文："+plain);
        String[] plains = plain.split(",");
        // 逗号分割，参数顺序：指令名称、用户id
        String directive = plains[0];
        String email = plains[1];
        User user = userRepository.findByEmail(email)
                .orElse(null);
        if(user == null){
            log.error("(重置密码接受端):  根据邮箱未查出任何数据 ");
            throw new UnacceptableException("失败");
        }

        String passwordEncrypted = getPasswordEncrypted(newPassword, user.getEmail());
        user.setPassword(passwordEncrypted);

        userRepository.save(user);

    }

    @Override
    public void reSetPwdSendEmailPre(ResetPwdDTO modifyEmailPreDTO) throws UnacceptableException{

        if(!userRepository.existsByEmail(modifyEmailPreDTO.getEmail())){
            throw new UnacceptableException("此邮箱未注册");
        }

        Map<String,String> map = new HashMap<>();
        // 逗号分割，参数顺序：指令名称、用户邮箱
        StringBuffer params = new StringBuffer();
        params.append(CommonConstants.RESET_PWD);
        params.append(",");
        params.append(modifyEmailPreDTO.getEmail());
        map.put("innerToken", AESUtil.GetAESCode(params.toString())); //由新邮箱加盐的密码md5

        String token = JWTTokenUtil.signWithMap(map,JWTTokenUtil.RESETPWD_EMAIL_EXP);
        StringBuffer sb = new StringBuffer();
        //拼接链接
        sb.append(modifyEmailPreDTO.getBasePath()).append("/user/pwd/put").append("?token=").append(token);

        String link = sb.toString();

        if(!EmailUtil.sendSingleEmailWithLinkAndTemplate(modifyEmailPreDTO.getEmail(),link,EmailUtil.RESET_PWD_TEMPLATE)){

            throw new UnacceptableException("邮件发送失败");
        }

    }

    @Override
    public String doLogin(LoginDTO loginDTO) throws UnacceptableException, BusinessException {

        User user = userRepository.findByEmail(loginDTO.getEmail())
                .orElseThrow(() -> new UnacceptableException("您还未注册"));

        if(Objects.equals(user.getStatus(), UserStatusEnum.NON_ACTIVATED.getUserStatus())){
            throw new UnacceptableException("请先激活您的邮箱");
        }

        if(Objects.equals(user.getStatus(),UserStatusEnum.FROZEN.getUserStatus())){
            throw new UnacceptableException("账户冻结中");
        }

        String passwordEncrypted = getPasswordEncrypted(loginDTO.getPassword(),loginDTO.getEmail());

        if(!Objects.equals(passwordEncrypted,user.getPassword())){
            throw new UnacceptableException("密码错误");
        }
        //todoo 登录表记录登录数据。。。

        String token = JWTTokenUtil.sign(String.valueOf(user.getId()), loginDTO.getIp(), JWTTokenUtil.DEF_EXP);

        return token;
    }
    // 不加入注解会导致没有事务，加入注解会导致dubbo的service失效
    //@Transactional(rollbackFor = Exception.class)
    @Override
    public void doRegister(LoginDTO loginDTO,String basePath) {

        String password = loginDTO.getPassword();
        String email = loginDTO.getEmail();

        User userFromDB = userRepository.findByEmail(loginDTO.getEmail()).orElse(null);

        // 如果用户存在与系统中
        if(userFromDB != null){
            UserStatusEnum userStatusEnum = UserStatusEnum.getUserStatusEnum(userFromDB.getStatus());
            if(userStatusEnum == null){
                log.error("用户状态值异常");
                throw new UnacceptableException("系统异常");
            }
            switch (userStatusEnum){
                case NON_ACTIVATED:
                    //如果状态为3，未激活注册，则先将未激活的用户记录删除
                    userRepository.deleteById(Long.valueOf(userFromDB.getId()));
                    break;
                case FROZEN:
                    throw new UnacceptableException("该账户存在且是冻结状态");
                case NORMAL:
                    throw new UnacceptableException("该邮箱已被注册");
            }
        }

        User  user = new User();
        user.setEmail(loginDTO.getEmail());
        //密码加密算法： Md5(Md5(password)+email) 所以用户换邮箱时密码也要更新
        String passwordEncrypted = getPasswordEncrypted(password,email);

        user.setPassword(passwordEncrypted);
        user.setStatus(UserStatusEnum.NON_ACTIVATED.getUserStatus()); //表示邮箱还未激活
        user.setCreateAt(System.currentTimeMillis());

        User user1 = userRepository.save(user);

        /**
         * 发送激活邮件
         */
        log.info("user1---->>： "+user1);
        if(user1.getId() == null){
            throw new BusinessException("用户id为null");
        }

        loginDTO.setUserId(user1.getId()+"");
        if(!sendEmail(loginDTO,basePath)){
            throw new BusinessException("邮件发送失败，请重试或联系相关人员");
        }

    }

    //密码加密算法： Md5(Md5(password)+email) 所以用户换邮箱时密码也要更新
    public String getPasswordEncrypted(String password,String email){
        return Md5Util.string2MD5(Md5Util.string2MD5(password)+email);
    }

    @Override
    public void activeEmail(String token) {
        if(!JWTTokenUtil.verifyWithNoId(token)){
            throw new UnacceptableException("链接已过期或无效");
        }

        String userId = JWTTokenUtil.getUserId(token);
        if(userId == null){
            throw new UnacceptableException("无效token，没有用户id");
        }
        User user = userRepository.findById(Long.valueOf(userId))
                .orElseThrow(() -> new BusinessException("无此用户信息"));
        user.setStatus(0); //将用户状态设为正常
        userRepository.save(user);

    }

    @Override
    public void postUserBaseInfo(UserBaseInfoDTO userBaseInfoDTO, Long userId) {

        User user = userRepository.findById(userId).orElseThrow(() -> new DaoException("要操作的用户不存在"));

        // 如果前端传空值字段进来，用户信息里原有值字段将被覆盖为空值
        BeanMapper.copy(userBaseInfoDTO,user);

        userRepository.save(user);

    }

    public void putUserAvatar(UserAvatarRequestDTO avatarRequestDTO, Long userId) throws DaoException{

        User user = userRepository.findById(userId).orElseThrow(() -> new DaoException("要操作的用户不存在"));
        user.setAvatar(avatarRequestDTO.getAvatar());
        userRepository.save(user);
    }


    @Override
    @Transactional(readOnly = true)
    public UserBaseInfoDTO getUserBaseInfo(Long userId) {

        User user = userRepository.findById(userId).orElseThrow(() -> new DaoException("要操作的用户不存在"));

        return BeanMapper.map(user,UserBaseInfoDTO.class);
    }

    @Override
    public void modifyPassword(PasswordDTO passwordDTO, Long userId) {

        User user = userRepository.findById(userId).orElseThrow(() -> new DaoException("要操作的用户不存在"));

        if(!Objects.equals(user.getPassword(),getPasswordEncrypted(passwordDTO.getOldPwd(),user.getEmail()))){
            throw new UnacceptableException("原密码输入错误");
        }

        user.setPassword(getPasswordEncrypted(passwordDTO.getNewPwd(),user.getEmail()));
        userRepository.save(user);

    }


    @Override
    public void modifyEmailPre(ModifyEmailPreDTO modifyEmailPreDTO) {

        User user = userRepository.findById(modifyEmailPreDTO.getUserId()).orElseThrow(() -> new DaoException("要操作的用户不存在"));
        if(Objects.equals(modifyEmailPreDTO.getEmail(),user.getEmail())){
            throw new UnacceptableException("新邮箱地址与原邮箱相同");
        }

        String oldPasswordEncrypted = getPasswordEncrypted(modifyEmailPreDTO.getPassWord(),user.getEmail());
        String newPasswordEncrypted = getPasswordEncrypted(modifyEmailPreDTO.getPassWord(),modifyEmailPreDTO.getEmail());
        if(!Objects.equals(oldPasswordEncrypted,user.getPassword())){
            throw new UnacceptableException("密码错误");
        }

        Map<String,String> map = new HashMap<>();
        // 逗号分割，参数顺序：新邮箱、用户id、新密码md5
        StringBuffer params = new StringBuffer();
        params.append(modifyEmailPreDTO.getEmail());
        params.append(",");
        params.append(modifyEmailPreDTO.getUserId());
        params.append(",");
        params.append(newPasswordEncrypted);
        map.put("innerToken", AESUtil.GetAESCode(params.toString())); //由新邮箱加盐的密码md5

        String token = JWTTokenUtil.signWithMap(map,JWTTokenUtil.EMAIL_ACTIVE_EXP);
        StringBuffer sb = new StringBuffer();
        //拼接链接
        sb.append(modifyEmailPreDTO.getBasePath())
                .append("/user/email/modify")
                .append("?token=").append(token)
                .append("&loginToken=").append(modifyEmailPreDTO.getLoginToken());

        String link = sb.toString();

        if(!EmailUtil.sendSingleEmailLink(modifyEmailPreDTO.getEmail(),link)){

            throw new UnacceptableException("邮件发送失败");
        }

    }

    @Override
    public void modifyEmail(String token) throws UnacceptableException {
        if(!JWTTokenUtil.verifyWithNoId(token)){
            throw new UnacceptableException("链接已过期或无效");
        }

        String innerToken = JWTTokenUtil.getInfo(token,"innerToken");
        if(StringUtils.isEmpty(innerToken)){
            throw new UnacceptableException("无效token，信息不全");
        }
        String plain = "";
        //获取解密后的明文
      try {
         plain = AESUtil.RevertAESCode(innerToken);
      }catch (Exception e){
          log.error("aes解密失败");
          throw new UnacceptableException("无效token");
      }
      log.info("解密后的明文："+plain);
        String[] plains = plain.split(",");
        // 逗号分割，参数顺序：新邮箱、用户id、新密码md5
        String newEmail = plains[0];
        Long userId = Long.valueOf(plains[1]);
        String newPasswordEncrypted = plains[2];
        User user = userRepository.findById(Long.valueOf(userId))
                .orElseThrow(() -> new BusinessException("无此用户信息"));

        user.setEmail(newEmail);
        user.setPassword(newPasswordEncrypted);

        userRepository.save(user);


    }


    private boolean sendEmail(LoginDTO loginDTO,String basePath){

        String token = JWTTokenUtil.sign(loginDTO.getUserId(),loginDTO.getIp(),JWTTokenUtil.EMAIL_ACTIVE_EXP);
        StringBuffer sb = new StringBuffer();
        //拼接链接
        sb.append(basePath).append("/email/activation").append("?token=").append(token);

        String link = sb.toString();

        if(!EmailUtil.sendSingleEmailLink(loginDTO.getEmail(),link)){

            return false;
        }

        return true;
    }

    @Override
    public void saveUser(List users) {

        log.info("得到的结果： "+userRepository.saveAll(users));

    }

    // test

    public static void main(String[] args) {
        System.out.println(Md5Util.string2MD5(Md5Util.string2MD5("acer4745")+"1322364968@qq.com"));

    }
}
